package com.xmlt.core.template.generation;

import java.util.ArrayList;
import java.util.List;

import javax.xml.xpath.XPathExpressionException;

import ognl.OgnlException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.xmlt.core.template.TemplateException;
import com.xmlt.core.template.TemplateManager;
import com.xmlt.core.template.TemplateSection;
import com.xmlt.core.template.Utils;
import com.xmlt.core.template.expression.Expression;
import com.xmlt.core.template.expression.ExpressionFactory;
import com.xmlt.core.template.instance.Instance;

public class GenerationTemplate {
	private String reference;
	private TemplateManager manager;
	private Document document;
	private List<GenerationDescriptorDefinition> descriptorDefinitions;
	private String outputFolderPath;
	public GenerationTemplate(String ref, TemplateManager tm) throws TemplateException {
		reference = ref;
		manager = tm;
		descriptorDefinitions = new ArrayList<GenerationTemplate.GenerationDescriptorDefinition>();
		document = tm.getResolver().find(reference);
		loadDescriptors();
	}
	
	public Document getDocument() {
		return document;
	}
	
	public String getGenerationPath() throws TemplateException {
		return outputFolderPath;
	}
	
	public List<IGenerationDescriptor> getDescriptorsForInstance(Instance ins) throws TemplateException {
		List<IGenerationDescriptor> descriptors = new ArrayList<IGenerationDescriptor>();
		for (GenerationDescriptorDefinition dd: descriptorDefinitions) {
			try {
				if (dd.willGenerateInstance(ins)) {
					descriptors.add(dd.getDescriptor(ins));
				}
			} catch (OgnlException e) {
				throw new TemplateException(e);
			}
		}
		return descriptors;
	}
	
	private void loadDescriptors() throws TemplateException {
		try {
			NodeList nl = Utils.evaluateXPath(document, "/generation-template/outputFolder");
			outputFolderPath = nl.item(0).getTextContent();
		} catch (XPathExpressionException e) {
			throw new TemplateException(e);
		}
		try {
			NodeList nl = Utils.evaluateXPath(document, "/generation-template/descriptor");
			for (int i = 0; i < nl.getLength(); i++) {
				descriptorDefinitions.add(new GenerationDescriptorDefinition((Element) nl.item(i)));
			}
		} catch (Exception e) {
			throw new TemplateException(e);
		}
	}
	
	class GenerationDescriptorDefinition {
		private Element descriptorEl;
		private TemplateSection fileNameSection;
		private TemplateSection bodySection;
		private Expression filterExpression;
		private String templateRef;
		GenerationDescriptorDefinition(Element el) throws XPathExpressionException, TemplateException, OgnlException {
			descriptorEl = el;
			init();
		}
		
		private void init() throws XPathExpressionException, TemplateException, OgnlException {
			templateRef = descriptorEl.getAttribute("templateRef");
			NodeList filterNodeList = Utils.evaluateXPath(descriptorEl, "filter");
			String filterString = filterNodeList.item(0).getTextContent();
			if (filterString == null || "".equals(filterString.trim())) {
				filterString = "true";
			}
			filterExpression = ExpressionFactory.getExpression(filterString);
			NodeList fileNameNodeList = Utils.evaluateXPath(descriptorEl, "path");
			fileNameSection = new TemplateSection("path", (Element) fileNameNodeList.item(0));
			NodeList bodyNodeList = Utils.evaluateXPath(descriptorEl, "body");
			bodySection = new TemplateSection("display", (Element) bodyNodeList.item(0));
		}
		
		boolean willGenerateInstance(Instance ins) throws OgnlException {
			if (ins.getTemplate().getReference().equals(templateRef)) {
				return (Boolean) filterExpression.evaluate(ins, true);
			}
			
			return false;
		}
		
		IGenerationDescriptor getDescriptor(final Instance ins) {
			return new IGenerationDescriptor() {
				
				@Override
				public String getPath() {
					return outputFolderPath;
				}
				
				@Override
				public String getFileName() throws TemplateException {
					try {
						return fileNameSection.generate(ins);
					} catch (OgnlException e) {
						throw new TemplateException(e);
					}
				}
				
				@Override
				public Instance getInstance() {
					return ins;
				}
				
				@Override
				public TemplateSection getGeneratingSection() {
					return bodySection;
				}

			};
		}
	}
	
}
